#include "PlayerEntity.hpp"
#include "../MessageDispatcher.hpp"
#include "../MMessage.hpp"
#include "../InputEvent.hpp"
#include "../Collisions/BoundingSphere.hpp"
#include "../Camera/CameraManager.hpp"
#include "../Camera/ViewFrustum.hpp"
#include "../ResourceManagers/Mesh3D.hpp"
#include "PlayerEntityStates.hpp"
#include "PlasmaCannon.hpp"
#include <iostream>

using namespace std;


PlayerEntity::PlayerEntity(std::string modelName, std::string textureName, float fSpeed, MVector3D initPos) : Entity3D(modelName, textureName)
{
	m_eType = DYNAMIC;
	m_fSpeed = fSpeed;
	m_fFireRate = 0.0f;
	m_fAutocannonDmg = 1.0f;
	m_fHitPoints = 10.0f;
	m_fReceivedDmg = 0.0f;
	
	setPosition(initPos);

	m_pMFSMachine = new MFSMachine<PlayerEntity, PlayerEntityStateName>(this);
	m_pMFSMachine->setCurrentState(new PlayerIdle());
	m_pMFSMachine->setListenerState(new PlayerListen());

	MVector3D cannonPos = initPos;
	float fCannonOffset = const_cast<Mesh3D*>(getMesh())->getMinimum(Z_AXIS);
	cannonPos.z += fCannonOffset;
	int iFireRate = 500;
	float fDamage = 2.0f;
	float fBulletSpeed = 1.0f;
	m_pPCannon = new PlasmaCannon(cannonPos, iFireRate, fDamage, fBulletSpeed);

	float minZ = const_cast<Mesh3D*>(getMesh())->getMinimum(Z_AXIS);
	MVector3D cannonOffset;
	cannonOffset.z = minZ;
	m_pPCannon->setOffset(cannonOffset);
}

PlayerEntity::~PlayerEntity()
{
	delete m_pMFSMachine;
	m_pMFSMachine = NULL;
}

void PlayerEntity::setSpeed(float &fSpeed)
{
	m_fSpeed = fSpeed;
}

void PlayerEntity::setFireRate(float &fFRate)
{
	m_fFireRate = fFRate;
}

void PlayerEntity::update(clock_t tick)
{
	//if(CameraManager::GetInstance()->GetActiveCamera()->getFrustum()->isInFrustum(getBBox()) != INSIDE)
	//	return;

	cout << "player pos: " << getPosition().z << endl;
	m_pPCannon->setShotStartPos(getPosition());
	static bool dead = false;
	//static clock_t ticker = 0;
	PlayerEntity::getStateMachine()->updateMFSM(tick);
	m_pPCannon->update(tick);
	MVector3D translation;

	if(getStateMachine()->getCurrentState()->getStateName() == PLESN_EXPLODE ||
		getStateMachine()->getCurrentState()->getStateName() == PLESN_DIE)
		setVisibility(false);
	/*if(newPosition.z >= 10.0f && !dead)
	{
		dead = true;
		getStateMachine()->changeState(new HostileDie);
	}
	else*/
	{
		translation.z -= (tick * (m_fSpeed/2.0f))/1000.0f;
		this->translate(translation);
	}
}

bool PlayerEntity::shotAutocannon(Entity3D *pTargetEntity)
{
	bool bSuccess = false;	

	//! Suppose to be line-sphere collision detection but it's broken.
	//! TODO: fix it
	//float d;
	//MVector3D center = pTargetEntity->generateBSV()->getCenter();
	//float     fRadius = pTargetEntity->generateBSV()->getRadius();
	//MVector3D shotDir; shotDir.Set(0.0f, 0.0f, -1.0f);

	//float fShotDirCenter = shotDir.DotProduct(center);

	//d = fShotDirCenter*fShotDirCenter - center.DotProduct(center) + fRadius*fRadius;

	//if(d < 0)
	//	return bSuccess;
	//else if(d > 0)
	//{
	//	bSuccess = true;
	//	MMessage* pMsg = new MMessage(MSG_CMD_GET_DAMAGE, 0, NULL);
	//	MessageDispatcher::getInstance()->dispatchMessage(pMsg, getEntityID(), pTargetEntity->getEntityID());

	//	//set burst cooldown m_iBurstCooldown to introduce small interval between generation of MSG_CMD_GET_DAMAGE messages;
	//	//otherwise target will receive multiple messages in very small amount of time and will enter DIE state immediately
	//	//after being hit which we don't want
	//}

	//! To check if autocannon hits entity its faster and simplier to check if autocannon position is in bounds of circle's
	//! diameter.

	float fGunPos = getPosition().x;
	float fTargetPos = pTargetEntity->getPosition().x;
	BoundingSphere* pBSV = pTargetEntity->generateBSV();
	float fRadius = pBSV->getRadius();
	if(fGunPos <= (fTargetPos+fRadius) && fGunPos >= (fTargetPos-fRadius))
	{
		bSuccess = true;
		int iDamage = 2;
		MMessage* pMsg = new MMessage(MSG_CMD_GET_DAMAGE, 0, &iDamage);
		MessageDispatcher::getInstance()->dispatchMessage(pMsg, getEntityID(), pTargetEntity->getEntityID());
	}

	return bSuccess;
}

void PlayerEntity::handleEvent(InputEvent *pEvent)
{
	MSG_COMMAND command = MSG_CMD_ERROR;

	void* msgData;

	if(pEvent->m_eEventType == KEYBOARD_EVENT_T)
	{
		command = MSG_CMD_KEYPRESS;
		msgData = &pEvent->m_iKeyCode;
	}
	else if(pEvent->m_eEventType == MOUSE_EVENT_T)
	{
		command = MSG_CMD_MOUSE;
		msgData = &pEvent->m_iKeyCode; //change in future
	}

	MMessage* pMsg = new MMessage(command, 0, msgData);

	getStateMachine()->handleMessage(pMsg);
}

MFSMachine<PlayerEntity, PlayerEntityStateName>* PlayerEntity::getStateMachine()
{
	return m_pMFSMachine;
}

PlasmaCannon* PlayerEntity::getCannon()
{
	return m_pPCannon;
}

//EOF
